#include "Timer.h"
#include <iostream>
#include <sys/time.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
using namespace std;

CTimer::CTimer()
{
    userCallBack  = NULL ;
    userParm      = NULL ;
	userFrequency = 2 ;
}

CTimer::CTimer (TimerProc userProc, void * pParm, long inFrequency)
{
    userCallBack  = userProc ;
    userParm      = pParm    ;
	userFrequency = inFrequency ;
}

void CTimer::startTimer( int time )
{
    if ( (time <= 0) || (userCallBack == NULL) || (userFrequency < 2) )
        return ;

    deltaTime = time ;
    counter   = 0    ;

    //Init the rtc
    rtcFD = open("/dev/rtc", O_RDONLY);
    if(rtcFD < 0)
    {
        cout<<"TimerWarning: open /dev/rtc error..."<<endl;
        return;
    }
    
    //Set frequency in HZ, numbered 2^n. non-root user can set from 2 to 64.
    //root user can be larger.
    if(ioctl(rtcFD, RTC_IRQP_SET, userFrequency) < 0)
    {
        cout<<"TimerWarning: Set rtc request failed..."<<endl;
        close(rtcFD);
        return;
    }

    TimerEntry * pTE = new TimerEntry ;
    pTE -> pCTimerObject = this ;
    pTE -> pCallBackParm = userParm ;

    if(ioctl(rtcFD, RTC_PIE_ON, 0) < 0)
    {
        cout<<"TimerWarning: ioctl(RTC_PIE_ON) failed..."<<endl;
        close(rtcFD);
        return;
    }
	
    pthread_create(&timerThreadID, NULL, threadRoutineWrapper, (void *)pTE );
}
    
void CTimer::stopTimer()
{
    if (!(rtcFD > 0))
    {
        cout<<"TimerWarning: rtcfd < 0...Stop failed..."<<endl;
        return;
    }

    if(ioctl(rtcFD, RTC_PIE_OFF, 0) < 0)
    {
        cout<<"TimerWarning: ioctl(RTC_PIE_ON) failed..."<<endl;
        close(rtcFD);
        return;
    }

    pthread_cancel ( timerThreadID );
    pthread_join   ( timerThreadID, NULL );

    close ( rtcFD ) ;
}

void * CTimer::threadRoutineWrapper ( void * pTimerEntry )
{
    TimerEntry * pTE = static_cast<TimerEntry *>(pTimerEntry) ;
    void * pRet = (pTE->pCTimerObject) -> threadRoutine ( pTE -> pCallBackParm ) ;
    delete pTE ;
}

void * CTimer::threadRoutine ( void * pParm )
{
    while(true)
    {
        read ( rtcFD, &data, sizeof(unsigned long) );

        counter++ ;

        if ( isTimeout() )
        {
            userCallBack ( userParm ) ;
            counter = 0;
        }
        pthread_testcancel();
    }

    return NULL ;
}

CTimer::~CTimer()
{
    pthread_cancel ( timerThreadID );
    pthread_join   ( timerThreadID, NULL );

    close          ( rtcFD );
}

bool CTimer::isTimeout ( )
{
    return deltaTime * 2 <= counter ;
}
